package cn.core.kpis.service;

import cn.core.kpis.eo.DataSource;
import cn.core.kpis.mapper.DataSourceMapper;
import cn.core.kpis.qo.DataSourceQO;
import cn.core.kpis.vo.DataSourceVO;
import cn.core.tool.ao.DataExportAO;
import cn.core.tool.context.CoreContextData;
import cn.core.tool.event.CoreDataEvent;
import cn.core.tool.exception.CoreException;
import cn.core.tool.service.FileService;
import cn.core.tool.util.CoreTool;
import cn.core.tool.util.Paging;
import cn.core.tool.util.Param;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.HikariDataSourceCreator;
import com.baomidou.dynamic.datasource.ds.ItemDataSource;
import com.baomidou.dynamic.datasource.enums.SeataMode;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.hikari.HikariCpConfig;
import com.google.gson.reflect.TypeToken;
import lombok.extern.log4j.Log4j2;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;

/**
 * 数据源 服务
 */
@Log4j2
@Service
public class DataSourceService {

  @Resource
  private ApplicationEventPublisher applicationEventPublisher;

  @Resource
  private FileService fileService;

  @Resource
  private DataSourceMapper dataSourceMapper;

  @Resource
  private javax.sql.DataSource dataSource;

  /**
   * 数据源 >>> 保存
   */
  @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
  public DataSource save(DataSource eo) throws Exception {
    try {
      if (CoreTool.isNullEo(eo)) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      eo.setId(CoreTool.getUUID32());
      dataSourceMapper.save(eo);
      return eo;
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 批量保存
   */
  @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
  public Collection<DataSource> saveBatch(Collection<DataSource> eos) throws Exception {
    try {
      if (CoreTool.isEmpty(eos)) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      for (DataSource eo : eos) {
        if (CoreTool.isNullEo(eo)) {
          throw new CoreException(CoreException.E_PARAM_ERR);
        }
        eo.setId(CoreTool.getUUID32());
      }
      dataSourceMapper.saveBatch(new Paging<>(), eos);
      return eos;
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 修改
   */
  @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
  public DataSource update(DataSource eo) throws Exception {
    try {
      if (CoreTool.isNullEo(eo) || CoreTool.isNullStr(eo.getId())) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      DataSource oeo = dataSourceMapper.pk(new DataSourceQO().setId(eo.getId()));
      if (CoreTool.isNullEo(oeo)) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
//      if (eo.getVersions() != null ? !eo.getVersions().equals(oeo.getVersions()) : oeo.getVersions() != null ? true : false) {
//        throw new CoreException(CoreException.E_VERSIONS_ERR);
//      }
      dataSourceMapper.update(eo);
      return eo;
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 批量修改
   */
  @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
  public Collection<DataSource> updateBatch(Collection<DataSource> eos) throws Exception {
    try {
      if (CoreTool.isEmpty(eos)) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      for (DataSource eo : eos) {
        if (CoreTool.isNullEo(eo) || CoreTool.isNullStr(eo.getId())) {
          throw new CoreException(CoreException.E_PARAM_ERR);
        }
        DataSource oeo = dataSourceMapper.pk(new DataSourceQO().setId(eo.getId()));
        if (CoreTool.isNullEo(oeo)) {
          throw new CoreException(CoreException.E_PARAM_ERR);
        }
//        if (eo.getVersions() != null ? !eo.getVersions().equals(oeo.getVersions()) : oeo.getVersions() != null ? true : false) {
//          throw new CoreException(CoreException.E_VERSIONS_ERR);
//        }
      }
      dataSourceMapper.updateBatch(new Paging<>(), eos);
      return eos;
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 删除
   */
  @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
  public void remove(DataSource eo) throws Exception {
    try {
      if (CoreTool.isNullEo(eo) || CoreTool.isNullStr(eo.getId())) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      dataSourceMapper.remove(eo);
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 批量删除
   */
  @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
  public void removeBatch(Collection<DataSource> eos) throws Exception {
    try {
      if (CoreTool.isEmpty(eos)) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      for (DataSource eo : eos) {
        if (CoreTool.isNullEo(eo) || CoreTool.isNullStr(eo.getId())) {
          throw new CoreException(CoreException.E_PARAM_ERR);
        }
      }
      dataSourceMapper.removeBatch(eos);
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 数据量
   */
  public Long count(DataSourceQO qo) throws Exception {
    try {
      return dataSourceMapper.count(qo);
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 主键查询
   */
  public DataSource pk(DataSourceQO qo) throws Exception {
    try {
      if (CoreTool.isNullEo(qo) || CoreTool.isNullStr(qo.getId())) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      return dataSourceMapper.pk(qo);
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 查询
   */
  public Collection<DataSource> query(DataSourceQO qo) throws Exception {
    try {
      return dataSourceMapper.query(qo);
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 分页
   */
  public Paging<DataSource> paging(Param<DataSourceQO> param) throws Exception {
    try {
      if (CoreTool.isNullEo(param)) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      if (param.getPage() < 1 || param.getRows() < 1) {
        throw new CoreException(CoreException.E_PAGING_PARAM_ERR);
      }
      Paging<DataSource> paging = new Paging<>(param);
      if (CoreTool.isEmpty(param.getData()) || CoreTool.isNullEo(param.get())) {
        Long count = dataSourceMapper.count(null);
        paging.setCount(count);
        if (count > 0) {
          if (count <= paging.getOffset()) {
            throw new CoreException(CoreException.E_PAGING_PARAM_ERR);
          }
          paging.setData(dataSourceMapper.paging(paging, null));
        }
      } else {
        Long count = dataSourceMapper.count(param.get());
        paging.setCount(count);
        if (count > 0) {
          if (count <= paging.getOffset()) {
            throw new CoreException(CoreException.E_PAGING_PARAM_ERR);
          }
          paging.setData(dataSourceMapper.paging(paging, param.get()));
        }
      }
      return paging;
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 业务数据量
   */
  public Long countVo(DataSourceQO qo) throws Exception {
    try {
      return dataSourceMapper.countVo(qo);
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 业务主键查询
   */
  public DataSourceVO pkVo(DataSourceQO qo) throws Exception {
    try {
      if (CoreTool.isNullEo(qo) || CoreTool.isNullStr(qo.getId())) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      return dataSourceMapper.pkVo(qo);
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 业务查询
   */
  public Collection<DataSourceVO> queryVo(DataSourceQO qo) throws Exception {
    try {
      return dataSourceMapper.queryVo(qo);
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 业务分页
   */
  public Paging<DataSourceVO> pagingVo(Param<DataSourceQO> param) throws Exception {
    try {
      if (CoreTool.isNullEo(param)) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      if (param.getPage() < 1 || param.getRows() < 1) {
        throw new CoreException(CoreException.E_PAGING_PARAM_ERR);
      }
      Paging<DataSourceVO> paging = new Paging<>(param);
      if (CoreTool.isEmpty(param.getData()) || CoreTool.isNullEo(param.get())) {
        Long count = dataSourceMapper.countVo(null);
        paging.setCount(count);
        if (count > 0) {
          if (count <= paging.getOffset()) {
            throw new CoreException(CoreException.E_PAGING_PARAM_ERR);
          }
          paging.setData(dataSourceMapper.pagingVo(paging, null));
        }
      } else {
        Long count = dataSourceMapper.countVo(param.get());
        paging.setCount(count);
        if (count > 0) {
          if (count <= paging.getOffset()) {
            throw new CoreException(CoreException.E_PAGING_PARAM_ERR);
          }
          paging.setData(dataSourceMapper.pagingVo(paging, param.get()));
        }
      }
      return paging;
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 数据导入
   */
  public Collection<DataSource> dataImport(String ao) throws Exception {
    try {
      if (CoreTool.isNullStr(ao)) {
        throw new CoreException(CoreException.E_DATA_IMPORT_FILE_URL_IS_NULL_ERR);
      }
      List<DataSource> allData = null;
      try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(fileService.getFileStreamByFileUrl(ao))) {
        ExcelReader reader = ExcelUtil.getReader(byteArrayInputStream);
        reader.addHeaderAlias("数据源名称", "dataSourceName");
        reader.addHeaderAlias("数据源标识", "dataSourceKey");
        reader.addHeaderAlias("数据库类型", "dbType");
        reader.addHeaderAlias("IP地址", "ipAddress");
        reader.addHeaderAlias("端口", "port");
        reader.addHeaderAlias("服务名", "serverName");
        reader.addHeaderAlias("数据库名", "dbName");
        reader.addHeaderAlias("用户名", "username");
        reader.addHeaderAlias("密码", "password");
        reader.addHeaderAlias("描述", "description");
        reader.addHeaderAlias("排序权重", "weightOrder");
        allData = reader.readAll(DataSource.class);
        if (CoreTool.isEmpty(allData)) {
          throw new CoreException(CoreException.E_IMPORT_DATA_IS_NULL_ERR);
        }
//        CoreDataEvent typeCoreDataEvent = new CoreDataEvent("getDictNameValueMapInfoByDictKey", "type");
//        applicationEventPublisher.publishEvent(typeCoreDataEvent);
//        LinkedHashMap<String, String> typeDictNameValueMapInfo = CoreTool.isNotNullStr(typeCoreDataEvent.getData()) ? typeCoreDataEvent.getData(new TypeToken<LinkedHashMap<String, String>>(){}) : new LinkedHashMap<String, String>();
        for (DataSource eo : allData) {
          eo.setId(CoreTool.getUUID32());
//          eo.setType(typeDictNameValueMapInfo.get(eo.getType()));
        }
        dataSourceMapper.saveBatch(new Paging<>(), allData);
      } catch (IOException e) {
        throw new CoreException(CoreException.E_ERR);
      }
      return allData;
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源 >>> 数据导出
   */
  public String dataExport(DataExportAO<DataSourceQO> ao) throws Exception {
    try {
      if (CoreTool.isEmpty(ao.getDataExportFieldConf())) {
        throw new CoreException(CoreException.E_DATA_EXPORT_FIELD_CONF_IS_NULL_ERR);
      }
      Collection<DataSourceVO> vos = dataSourceMapper.queryVo(ao.get());
      if (CoreTool.isEmpty(vos)) {
        throw new CoreException(CoreException.E_EXPORT_DATA_IS_NULL_ERR);
      }
//      CoreDataEvent typeCoreDataEvent = new CoreDataEvent("getDictValueMapInfoByDictKey", "type");
//      applicationEventPublisher.publishEvent(typeCoreDataEvent);
//      LinkedHashMap<String, String> typeDictValueMapInfo = CoreTool.isNotNullStr(typeCoreDataEvent.getData()) ? typeCoreDataEvent.getData(new TypeToken<LinkedHashMap<String, String>>(){}) : new LinkedHashMap<String, String>();
//      for (DataSourceVO eo : vos) {
//        eo.setType(typeDictValueMapInfo.get(eo.getType()));
//      }
      return fileService.getFileUrlByDataExportAO(new DataExportAO<DataSourceVO>()
          .setBigDataExport(ao.getBigDataExport())
          .setFileModule(ao.getFileModule())
          .setFileName(ao.getFileName())
          .setDataExportFieldConf(ao.getDataExportFieldConf())
          .setData(vos));
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源信息Id >>> 加载数据源
   */
  public void addDataSource(String id) throws Exception {
    try {
      if (CoreTool.isNullStr(id)) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      DataSource rDataSource = dataSourceMapper.pk(new DataSourceQO().setId(id));
      String driverClassName = null;
      String urlStr = null;
      if (CoreTool.mysql.equals(rDataSource.getDbType())) {
        driverClassName = "com.mysql.cj.jdbc.Driver";
        urlStr = "jdbc:mysql://" + rDataSource.getIpAddress() + ":" + rDataSource.getPort() + "/" + rDataSource.getDbName() + "?useUnicode=yes&characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&allowMultiQueries=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true";
      } else if (CoreTool.oracle.equals(rDataSource.getDbType())) {
        driverClassName = "oracle.jdbc.driver.OracleDriver";
        urlStr = "jdbc:oracle:thin:@" + rDataSource.getIpAddress() + ":" + rDataSource.getPort() + "/" + rDataSource.getServerName();
      } else {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
      DataSourceProperty dataSourceProperty = new DataSourceProperty();
      dataSourceProperty.setPoolName(rDataSource.getDataSourceKey());
      dataSourceProperty.setDriverClassName(driverClassName);
      dataSourceProperty.setUrl(urlStr);
      dataSourceProperty.setUsername(rDataSource.getUsername());
      dataSourceProperty.setPassword(rDataSource.getPassword());
      dataSourceProperty.setLazy(false);
      HikariCpConfig hikariCpConfig = new HikariCpConfig();
      hikariCpConfig.setMinIdle(1);
      hikariCpConfig.setMaxPoolSize(5);
      javax.sql.DataSource newDataSource = new HikariDataSourceCreator(hikariCpConfig).createDataSource(dataSourceProperty);
      ItemDataSource newItemDataSource = new ItemDataSource(rDataSource.getDataSourceKey(), newDataSource, newDataSource, false, false, SeataMode.AT);
      ds.addDataSource(rDataSource.getDataSourceKey(), newItemDataSource);
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

  /**
   * 数据源信息Id >>> 卸载数据源
   */
  public void uninstallDataSource(String id) throws Exception {
    try {
      if (CoreTool.isNullStr(id)) {
        throw new CoreException(CoreException.E_PARAM_ERR);
      }
      DataSource rDataSource = dataSourceMapper.pk(new DataSourceQO().setId(id));
      DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
      ds.removeDataSource(rDataSource.getDataSourceKey());
    } catch (Exception e) {
      if (log.isDebugEnabled()) {
        log.error(e.getMessage(), e);
      }
      throw e;
    }
  }

}
